package pers.cz.util;

import java.util.Arrays;

/**
 * @program: Jef-log-tail
 * @description: 扩展JStringBuilder
 * 1、提供向前追加的功能
 * 实现原理：
 *         1、初始化数组，如果向前插入，则向前扩容，如果向后插入则向后扩容
 *         2、扩容后提供前缀指针和后缀指针来指向当前存储的位置
 * @author: Cheng Zhi
 * @create: 2023-03-22 14:01
 **/
public class JefStringBuilder {

    /**
     * The value is used for character storage.
     */
    char[] value;

    /**
     * The count is the number of characters used.
     */
    int count;

    /**
     * 前缀数组的指针，初始化为0
     */
    int preCursor;

    /**
     * 后缀数组的指针，初始化为0
     */
    int fixCursor = 0;

    /**
     * 初始化数组长度
     */
    int defaultCapacity = 16;

    /**
     * 前缀数组长度
     */
    int preArraySize;

    /**
     * The maximum size of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    JefStringBuilder() {
        value = new char[defaultCapacity];
    }

    JefStringBuilder(int capacity) {
        value = new char[capacity];
    }

    /**
     * 向后追加字符串
     * @param str
     * @return
     */
    public JefStringBuilder append(String str) {
        if (str == null)
            return appendNull();
        int len = str.length();
        ensureCapacityInternal(count + len, len);
        str.getChars(0, len, value, fixCursor);
        fixCursor = fixCursor + len;
        count += len;
        return this;
    }

    public JefStringBuilder append(StringBuffer sb) {
        if (sb == null)
            return appendNull();
        int len = sb.length();
        ensureCapacityInternal(count + len, len);
        sb.getChars(0, len, value, fixCursor);
        fixCursor = fixCursor + len;
        count += len;
        return this;
    }

    /**
     * @since 1.8
     */
    JefStringBuilder append(JefStringBuilder asb) {
        if (asb == null)
            return appendNull();
        int len = asb.length();
        ensureCapacityInternal(count + len, len);
        asb.getChars(0, len, value, count);
        count += len;
        return this;
    }

    public void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) {
        if (srcBegin < 0)
            throw new StringIndexOutOfBoundsException(srcBegin);
        if ((srcEnd < 0) || (srcEnd > count))
            throw new StringIndexOutOfBoundsException(srcEnd);
        if (srcBegin > srcEnd)
            throw new StringIndexOutOfBoundsException("srcBegin > srcEnd");
        System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
    }


    // Documentation in subclasses because of synchro difference
    public JefStringBuilder append(CharSequence s) {
        if (s == null)
            return appendNull();
        if (s instanceof String)
            return this.append((String)s);
        if (s instanceof JefStringBuilder)
            return this.append((JefStringBuilder)s);

        return this.append(s, 0, s.length());
    }

    public JefStringBuilder append(CharSequence s, int start, int end) {
        if (s == null)
            s = "null";
        if ((start < 0) || (start > end) || (end > s.length()))
            throw new IndexOutOfBoundsException(
                    "start " + start + ", end " + end + ", s.length() "
                            + s.length());
        int len = end - start;
        ensureCapacityInternal(count + len, len);
        for (int i = start, j = count; i < end; i++, j++)
            value[j] = s.charAt(i);
        count += len;
        return this;
    }

    public JefStringBuilder append(Object obj) {
        return append(String.valueOf(obj));
    }

    /**
     * 向前追加
     * @param str
     * @return
     */
    public JefStringBuilder preAppand(String str) {
        if (str == null) {
            return this;
        }
        int len = str.length();
        int newLength = count + len;
        preEnsureCapacityInternal(newLength, len);
        int length = value.length;
        preCursor = preCursor - len;
        str.getChars(0, len, value, preCursor);
        count += len;
        return this;
    }

    /**
     * 向前扩容
     * @param minimumCapacity 扩容的长度
     * @param len 当前字符串的长度，用来判断是否需要扩容
     */
    private void preEnsureCapacityInternal(int minimumCapacity,int len) {
        // 如果当前指针的位置减去将要存入数组的字符串长度，如果不够存，意味着要扩容
        if ((preCursor - len) < 0) {
            int persSize = newCapacity(minimumCapacity+len); // 多扩容一些，空间换时间
            // 数组向前扩容
            char[] copy = new char[persSize + count];
            System.arraycopy(value, 0, copy, persSize-value.length, value.length);
            int oldPreCursor = preCursor;
            preCursor = oldPreCursor + persSize - value.length ;
            fixCursor = fixCursor + preCursor - oldPreCursor;
            value = copy;
            copy = null;
        }
    }

    private JefStringBuilder appendNull() {
        int c = count;
        ensureCapacityInternal(c + 4, 4);
        final char[] value = this.value;
        value[c++] = 'n';
        value[c++] = 'u';
        value[c++] = 'l';
        value[c++] = 'l';
        count = c;
        return this;
    }

    /**
     * 向后扩容
     * @param minimumCapacity 扩容的长度
     * @param len 当前字符串的长度，用来判断是否需要扩容
     */
    private void ensureCapacityInternal(int minimumCapacity, int len) {

        if ((fixCursor + len-value.length) > 0) {
            value = Arrays.copyOf(value, newCapacity(minimumCapacity));
        }
    }

    /**
     * 计算新容量
     * @param minCapacity
     * @return
     */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int newCapacity = (value.length << 1) + 2;
        if (newCapacity - minCapacity < 0) {
            newCapacity = minCapacity;
        }
        return (newCapacity <= 0 || MAX_ARRAY_SIZE - newCapacity < 0)
                ? hugeCapacity(minCapacity)
                : newCapacity;
    }

    private int hugeCapacity(int minCapacity) {
        if (Integer.MAX_VALUE - minCapacity < 0) { // overflow
            throw new OutOfMemoryError();
        }
        return (minCapacity > MAX_ARRAY_SIZE)
                ? minCapacity : MAX_ARRAY_SIZE;
    }

    public String toString() {
        // Create a copy, don't share the array
        return new String(value, preCursor, count);
    }

    public int length() {
        return count;
    }

}

